use std::fmt::Debug;

use crate::gen_title;

pub fn ch4_2_1() {
    gen_title("ch4_2_1", 18);

    let x = 1;
    let sum = |y| x + y;

    assert_eq!(3, sum(2));

    println!("{}", sum(2));
}

pub fn ch4_2_2() {
    gen_title("ch4_2_2", 18);

    fn muuuuu(intensity: u32) -> u32 {
        println!("muuuu.....");
        // thread::sleep(Duration::from_secs(2));
        intensity
    }

    fn workout(intensity: u32, random_number: u32) {
        if intensity < 25 {
            println!("今天活力满满，先做{}个俯卧撑", muuuuu(intensity));
            println!(
                "旁边有妹子在看，俯卧撑太low，再来 {} 组卧推!",
                muuuuu(intensity)
            );
        } else if random_number == 3 {
            println!("昨天练过度了，今天还是休息下吧！");
        } else {
            println!(
                "昨天练过度了，今天干干有氧，跑步 {} 分钟!",
                muuuuu(intensity)
            );
        }
    }

    let intensity = 10;
    let random_number = 7;

    workout(intensity, random_number);
}

pub fn ch4_2_3() {
    gen_title("ch4_2_3", 18);

    fn muuuuu(intensity: u32) -> u32 {
        println!("muuuu.....");
        // thread::sleep(Duration::from_secs(2));
        intensity
    }

    fn workout(intensity: u32, random_number: u32) {
        let action = muuuuu;

        if intensity < 25 {
            println!("今天活力满满, 先做 {} 个俯卧撑!", action(intensity));
            println!(
                "旁边有妹子在看，俯卧撑太low, 再来 {} 组卧推!",
                action(intensity)
            );
        } else if random_number == 3 {
            println!("昨天练过度了，今天还是休息下吧！");
        } else {
            println!(
                "昨天练过度了，今天干干有氧, 跑步 {} 分钟!",
                action(intensity)
            );
        }
    }

    let intensity = 10;
    let random_number = 7;

    workout(intensity, random_number);
}

pub fn ch4_2_4() {
    gen_title("ch4_2_4", 18);

    fn workout(intensity: u32, random_number: u32) {
        let action = || {
            println!("muuuu.....");
            // thread::sleep(Duration::from_secs(2));
            intensity
        };

        if intensity < 25 {
            println!("今天活力满满, 先做 {} 个俯卧撑!", action());
            println!("旁边有妹子在看，俯卧撑太low, 再来 {} 组卧推!", action());
        } else if random_number == 3 {
            println!("昨天练过度了，今天还是休息下吧！");
        } else {
            println!("昨天练过度了，今天干干有氧, 跑步 {} 分钟!", action());
        }
    }

    let intensity = 10;
    let random_number = 7;

    workout(intensity, random_number);
}

pub fn ch4_2_5() {
    gen_title("ch4_2_5", 18);

    struct Cacher<T, E>
    where
        T: Fn(E) -> E,
        E: Debug,
    {
        query: T,
        value: Option<E>,
    }

    impl<T, E> Cacher<T, E>
    where
        T: Fn(E) -> E,
        E: Debug,
    {
        fn new(query: T) -> Cacher<T, E> {
            Cacher { query, value: None }
        }

        fn value(&mut self, arg: E) -> &E {
            match &self.value {
                Some(_) => self.value.as_ref().unwrap(),
                None => {
                    println!("value {:?} not exist, saving...", arg);
                    let v = (self.query)(arg);
                    self.value = Some(v);
                    self.value.as_ref().unwrap()
                }
            }
        }
    }

    let mut cacher = Cacher::new(|x| x + 1);

    println!("{}", cacher.value(4));
    println!("{}", cacher.value(4));

    let mut strCacher = Cacher::new(|x: String| x + "hahaha");

    println!("{}", strCacher.value("hello".to_string()));
    println!("{}", strCacher.value("hello".to_string()));
}

pub fn ch4_2_6() {
    gen_title("ch4_2_6", 18);

    let x = 4;
    let equal_to_x = |z| z == x;

    let y = 4;

    assert!(equal_to_x(y));
    println!("Success!");
}

pub fn ch4_2_7() {
    gen_title("ch4_2_7", 18);

    fn fn_once<F>(func: F)
    where
        F: FnOnce(usize) -> bool + Copy,
    {
        println!("{}", func(3));
        println!("{}", func(4));
    }

    let x = vec![1, 2, 3];
    fn_once(|z| z == x.len());
}

pub fn ch4_2_8() {
    gen_title("ch4_2_8", 18);

    let mut s = String::new();

    let mut update_string = |str| s.push_str(str);
    update_string("hello");

    println!("{}", s);
}

pub fn ch4_2_9() {
    gen_title("ch4_2_9", 18);

    let mut s = String::new();
    let update_string = |str| s.push_str(str);
    exec(update_string);

    println!("{}", s);

    fn exec<'a, F: FnMut(&'a str)>(mut f: F) {
        f("hello")
    }
}

pub fn ch4_2_10() {
    gen_title("ch4_2_10", 18);

    let s = "Hello, ".to_string();

    let update_string = |str| println!("{}, {}", s, str);
    exec(update_string);
    println!("{}", s);

    fn exec<'a, F: Fn(String) -> ()>(f: F) {
        f("world".to_string())
    }
}

pub fn ch4_2_11() {
    gen_title("ch4_2_11", 18);

    let s = "hello, world".to_string();
    let update_string = move || println!("{}", s);
    exec(update_string);

    fn exec<F: FnOnce()>(f: F) {
        f()
    }
}

pub fn ch4_2_12() {
    gen_title("ch4_2_12", 18);

    fn exec<F: FnOnce()>(f: F) {
        f()
    }

    fn exec1<F: FnMut()>(mut f: F) {
        f()
    }

    fn exec2<F: Fn()>(f: F) {
        f()
    }

    let s = "Hello, world";

    let update_string = || println!("{}", s);

    exec(update_string);
    exec1(update_string);
    exec2(update_string);
}

pub fn ch4_2_13() {
    gen_title("ch4_2_13", 18);

    fn factory(x: i32) -> Box<dyn Fn(i32) -> i32> {
        let num = 5;

        if x > 1 {
            Box::new(move |x| x + num)
        } else {
            Box::new(move |x| x - num)
        }
    }

    let f = factory(1);

    let answer = f(10);
    println!("{}", answer);
}
